#include "Semaphore.h"

using namespace std;

/*
 * Escribimos una implementación de semáforos sobre pthreads.
 * Usamos los wrapper del mutex y la variable de condición.
 * En este nivel, ya se oculta casi por completo la utilización
 * de los pthread. En los niveles superiores solo prestaremos
 * atención al algoritmo para el read write lock.
 */

Semaphore::Semaphore(int valor) {
	/* Es el valor inicial del semáforo */
	this->valor = valor;
	/* En esta variable se guardan los signals o wakeups */
	this->despertadas = 0;

	/* Nuestros wrappers: un mutex y una variable de condición */
	this->mutex = new Mutex();
	this->cond = new Cond();
}

Semaphore::~Semaphore() {
	/* Se libera la memoria solicitada para los wrappers */
	delete this->cond;	// Primero la variable de condición
	delete this->mutex;	// Después el mutex
}

void Semaphore::wait() {
	this->mutex->lock();

	--this->valor;

	if (this->valor < 0) {
		/*
		 * Se usa una estructura do..while para que el thread
		 * no tenga chance de despertarse a sí mismo.
		 * Nótese que con una estructura while..loop usual,
		 * un thread podría hacer signal y luego correr a
		 * esperarse y atrapar su propio signal. Sí, muy sutil...
		 */
		do {
			this->cond->wait(this->mutex);

		} while (this->despertadas < 1);

		--this->despertadas;
	}

	this->mutex->unlock();
}

void Semaphore::signal() {
	this->mutex->lock();

	++this->valor;

	if (this->valor <= 0) {

		++this->despertadas;

		this->cond->signal();
	}

	this->mutex->unlock();
}
